建立 S3 bucket,例如:photo-diary-frontend。
開啟 Public access(僅示範可讀,正式建議使用私有)。
在 Permissions 中的 Bucket Policy 中加入:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowLambdaUpload",
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::photo-diary-frontend/*"
},
{
"Sid": "AllowPublicRead",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::photo-diary-frontend/*"
}
]
}
Lambda 需要 s3:PutObject 權限,前端可以用 GET 讀取圖片。
語言:Python 3.9
功能:生成 S3 預簽名 URL
import json
import boto3
from botocore.exceptions import ClientError
S3_BUCKET = "photo-diary-frontend"
s3_client = boto3.client("s3", region_name="ap-east-2")
def lambda_handler(event, context):
# CORS 預檢
if event.get("httpMethod") == "OPTIONS":
return {
"statusCode": 200,
"headers": {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "*",
"Access-Control-Allow-Methods": "POST,GET,PUT,OPTIONS"
},
"body": ""
}
# POST:生成預簽名 URL
if event.get("httpMethod") == "POST":
try:
body = json.loads(event["body"])
filename = body.get("filename")
if not filename:
return {
"statusCode": 400,
"headers": {"Access-Control-Allow-Origin": "*"},
"body": json.dumps({"message": "Missing filename"})
}
upload_url = s3_client.generate_presigned_url(
"put_object",
Params={
"Bucket": S3_BUCKET,
"Key": f"photos/{filename}",
"ContentType": "image/jpeg"
},
ExpiresIn=300 # 5 分鐘
)
return {
"statusCode": 200,
"headers": {"Access-Control-Allow-Origin": "*"},
"body": json.dumps({"uploadURL": upload_url})
}
except ClientError as e:
print(e)
return {
"statusCode": 500,
"headers": {"Access-Control-Allow-Origin": "*"},
"body": json.dumps({"message": "Internal Server Error"})
}
return {
"statusCode": 405,
"headers": {"Access-Control-Allow-Origin": "*"},
"body": json.dumps({"message": "Method Not Allowed"})
}
使用 <input type="file">
選擇照片
預覽照片:FileReader.readAsDataURL()
按下「上傳」後:
上傳完成後可用 S3 URL 顯示照片
Access to fetch at ... from origin ... has been blocked by CORS policy
S3 Bucket 設定 CORS 後還是顯示 S3 不允許跨域,預簽名 URL 無法處理 OPTIONS,在S3的CORS中也無法使用OPTIONS,我真的試了快8小時還是一樣...非常崩潰呀
因為時間比較緊迫,我還是想把功能實現,所以想了一下解決方案,就是將照片轉成 Base64
字串,再存到 localStorage
或 IndexedDB
,不經過伺服器,也不用 S3 ,完全不會有 CORS 問題,但缺點就是資料全存在前端,雖然即使刷新還是會存在,但是就長期來看,儲存空間其實不太夠,還是比較不建議使啦!那這邊還是會繼續學習,到了能解決這個問題的時候我會再嘗試把功能上到AWS上!那麼明天我們就來看看他是如何存在前端的吧!